vue 生命周期

镇楼–生命周期图

生命周期

调试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
export default {
beforeCreate() {
debugger
console.log('beforeCreate', this)
console.log('this $data', this.$data) //null
},
data() {
debugger
console.log('data Oberserve', this)
return {
message: '测试数据'
}
},
created() {
debugger
console.log('created', this)
console.log('this $data', this.$data) //{message: '测试数据'}
},
beforeMount() {
debugger
console.log('beforeMount', this)
console.log('this $data', this.$el)// <div>{{message}}</div>
},
mounted() {
debugger
console.log('mounted', this)
console.log('this $data', this.$el) //<div>测试数据</div> 挂载
},
beforeUpdate() {
debugger
console.log('beforeUpdate', this)
},
updated() {
debugger
console.log('updated', this)
},
beforeDestroy() {
debugger
console.log('beforeDestroy', this)
},
destroyed() {
debugger
console.log('destroyed', this)
}
}

beforeCreate/created

beforeCreate 之前解析初始化on,attr

beforeCreate 之后才开始解析component的属性值绑定

created

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_init = function (options?: Object) {
initInternalComponent(vm, options)//vm.$options 创建render函数绑定
initProxy(vm) //代理设置
//...
initLifecycle(vm) //绑定$parent,$root,parent.$children
initEvents(vm)//事件绑定,v-on,v-model的input 指向 data.on 的别名
initRender(vm)//数据绑定 $attrs,$listeners,attr,data.on
callHook(vm, 'beforeCreate')
initInjections(vm) //解析inject
initState(vm) //data,methods,watch,computed,props 解析
initProvide(vm) //调用父级的provide
callHook(vm, 'created')
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}

beforeMount/mounted

1
2
3
4
5
6
7
8
9
10
11
12
13
function mountComponent (vm: Component,
el: ?Element,
hydrating?: boolean){
vm.$el = el //template,数据未被绑定
callHook(vm, 'beforeMount')
//...
vm._watcher = new Watcher(vm, updateComponent, noop)
if (vm.$vnode == null) { //没有template时候不触发
vm._isMounted = true
callHook(vm, 'mounted')
}
}

beforeUpdate/updated

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
_update = function (vnode: VNode, hydrating?: boolean) {
if (vm._isMounted) { //非初始化
callHook(vm, 'beforeUpdate')
}
// updates
vm.$el = vm.__patch__(prevVnode, vnode)//callUpdatedHooks 在watch的时候触发updated
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
function callUpdatedHooks (queue) {
let i = queue.length
while (i--) {
const watcher = queue[i]
const vm = watcher.vm
if (vm._watcher === watcher && vm._isMounted) {
callHook(vm, 'updated')
}
}
}

beforeDestroy/destroyed

调用$destroy函数触发,beforeDestroy后从父级移除,ob中移除,移除虚拟dom中数据

destroyed后解绑事件并把vue置为null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
callHook(vm, 'beforeDestroy')
// remove self from parent
// teardown watchers
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
// invoke destroy hooks on current rendered tree
vm.__patch__(vm._vnode, null)
// fire destroyed hook
callHook(vm, 'destroyed')
vm.$off()
}

activated/deactivated

该函数在keep-alive的时候调用,切换compoment时候将不会调用$destroy函数,初始化不调用该函数。
两个钩子函数与其他几个钩子函数调用不触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
export function activateChildComponent (vm: Component, direct?: boolean) {
if (direct) {
vm._directInactive = false
if (isInInactiveTree(vm)) {
return
}
} else if (vm._directInactive) {
return
}
if (vm._inactive || vm._inactive === null) {
vm._inactive = false
for (let i = 0; i < vm.$children.length; i++) {
activateChildComponent(vm.$children[i])
}
callHook(vm, 'activated')
}
}
export function deactivateChildComponent (vm: Component, direct?: boolean) {
if (direct) {
vm._directInactive = true
if (isInInactiveTree(vm)) {
return
}
}
if (!vm._inactive) {
vm._inactive = true
for (let i = 0; i < vm.$children.length; i++) {
deactivateChildComponent(vm.$children[i])
}
callHook(vm, 'deactivated')
}
}

到这里基本就完成了一次vue的组件生命周期,再回头看生命周期的图示更深刻理解。下面是在调试的时候遇见的笔记有趣的代码。在实现业务组件数据化配置时遇见的一些小坑

其他

$options initInternalComponent

可以从$option中获取关于组件的基本信息,eg.componetName,props,data

1
2
3
4
5
6
7
8
9
10
//vm.$options 创建render函数绑定,
initInternalComponent (vm: Component, options: InternalComponentOptions) {
const opts = vm.$options = Object.create(vm.constructor.options)
// doing this because it's faster than dynamic enumeration.
//...
if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}

$parent $children 设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//绑定$parent,$root,parent.$children
initLifecycle(vm: Component){
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$children = []
vm.$refs = {}
}

事件绑定,v-on指令事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
initEvents (vm: Component) {
//事件绑定,v-on指令事件
// init parent attached events
const listeners = vm.$options._parentListeners
updateComponentListeners(vm, listeners)
}
updateListeners (
on: Object,
oldOn: Object,
add: Function,
remove: Function,
vm: Component
){
//...
for (name in on) {
//...
event = normalizeEvent(name)////@,~! 修饰符 render函数中on使用[](https://cn.vuejs.org/v2/guide/render-function.html#%E4%BA%8B%E4%BB%B6-amp-%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6)
//...
add(event.name, cur, event.once, event.capture, event.passive)
//...
}
for (name in oldOn) {
if (isUndef(on[name])) {
event = normalizeEvent(name)
remove(event.name, oldOn[name], event.capture)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//数据绑定 $attrs,$listeners
function initRender (vm: Component) {
vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext)
vm.$scopedSlots = emptyObject
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
//
defineReactive(vm, '$attrs', parentData && parentData.attrs, null, true)
defineReactive(vm, '$listeners', parentData && parentData.on, null, true)
}
// 双向数据绑定实现的基础
function defineReactive ( obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean){
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
if (Array.isArray(value)) {
dependArray(value)
}
}
}
},
set: function reactiveSetter (newVal) {
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}

内部和外部使用的区别在于是否需要转换解析node,外部使用的render出现嵌套数组

需要更深入了解学习,需要学习Object.defineProperty,Proxy,Set,观察者模式,订阅者模式